#include "modbus.h"
#include "modbus_crc.h"
#include <string.h>
#include <stdio.h>
#include "uart.h"

MODBUS modbus;
uint16_t Reg[Reg_num];  // 寄存器数组

// Modbus初始化函数
void Modbus_Init() {

}

// Modbus 3号功能码函数：主机读取寄存器值
void Modbus_Func3() {
    uint16_t Regadd, Reglen, crc;
    uint8_t i = 0, j;
    // 得到要读取寄存器的首地址
    Regadd = modbus.rcbuf[2] * 256 + modbus.rcbuf[3];
    if (Regadd <= 3) {
        // 得到要读取寄存器的数据长度
        Reglen = modbus.rcbuf[4] * 256 + modbus.rcbuf[5];
        if (Regadd + Reglen >= 1 && Regadd + Reglen <= Reg_num) {
            // 发送回应数据包
            i = 0;
            modbus.sendbuf[i++] = modbus.myadd;          // 发送本机设备地址
            modbus.sendbuf[i++] = 0x03;                  // 发送功能码
            modbus.sendbuf[i++] = ((Reglen * 2) % 256);  // 返回字节个数
            for (j = 0; j < Reglen; j++)                 // 返回数据
            {
                modbus.sendbuf[i++] = Reg[Regadd + j] / 256;
                modbus.sendbuf[i++] = Reg[Regadd + j] % 256;
            }
            crc = CRC16(modbus.sendbuf, i);  // 计算要返回数据的CRC
            modbus.sendbuf[i++] = crc % 256;
            modbus.sendbuf[i++] = crc / 256;

            GPIO_SetBits(GPIOC, GPIO_Pin_7);  // RS485发送使能
            uartWriteBlocking(modbus.sendbuf, i);  // 发送数据
            while (DMA_GetCurrDataCounter(DMA1_Channel4) != 0);
            Delay_Ms(10);
            GPIO_ResetBits(GPIOC, GPIO_Pin_7);
        }
    }
    // 清空缓冲区
    memset(modbus.rcbuf, 0, sizeof(modbus.rcbuf));
    memset(modbus.sendbuf, 0, sizeof(modbus.sendbuf));
}

// Modbus 6号功能码函数：主机写入单个寄存器值
uint8_t Modbus_Func6(void) {
    uint16_t Regadd, val, crc = 0;
    uint8_t i = 0, k = 1;
    Regadd = modbus.rcbuf[2] * 256 + modbus.rcbuf[3];   // 得到要修改的地址
    if ((Regadd >= 0) && (Regadd < Reg_num)) {
        val = modbus.rcbuf[4] * 256 + modbus.rcbuf[5];  // 修改后的值
        Reg[Regadd] = val;                              // 修改本设备相应的寄存器

        // 回应主机
        modbus.sendbuf[i++] = modbus.myadd;  // 本设备地址
        modbus.sendbuf[i++] = 0x06;          // 功能码
        modbus.sendbuf[i++] = Regadd / 256;
        modbus.sendbuf[i++] = Regadd % 256;
        modbus.sendbuf[i++] = val / 256;
        modbus.sendbuf[i++] = val % 256;
        crc = CRC16(modbus.sendbuf, i);
        modbus.sendbuf[i++] = crc % 256;
        modbus.sendbuf[i++] = crc / 256;

        // 验证写入合法性
        switch (Regadd) {
            case 0:
                k = (Reg[Regadd] >= 1 && Reg[Regadd] <= 247) ? 0 : 1;
                break;
            case 1:
                k = (Reg[Regadd] <= 3) ? 0 : 1;
                break;
            default:
                k = 0;
                break;
        }

        GPIO_SetBits(GPIOC, GPIO_Pin_7);  // RS485发送使能
        uartWriteBlocking(modbus.sendbuf, i);  // 发送数据
        while (DMA_GetCurrDataCounter(DMA1_Channel4) != 0);
        Delay_Ms(10);
        GPIO_ResetBits(GPIOC, GPIO_Pin_7);
    } else {
        k = 2;
    }

    // 清空缓冲区
    memset(modbus.rcbuf, 0, sizeof(modbus.rcbuf));
    memset(modbus.sendbuf, 0, sizeof(modbus.sendbuf));
    
    return (k == 0) ? 2 : 3;
}

// Modbus 10号功能码函数（0x10）：写入多个寄存器
uint8_t Modbus_Func10(void) {
    uint16_t startAddr, regCount, byteCount;
    uint8_t i = 0, j = 0, k = 1;  // k=1表示失败，0表示成功
    uint16_t crc = 0;

    // 解析请求报文
    startAddr = (modbus.rcbuf[2] << 8) | modbus.rcbuf[3];  // 起始地址
    regCount = (modbus.rcbuf[4] << 8) | modbus.rcbuf[5];   // 寄存器数量
    byteCount = modbus.rcbuf[6];                           // 数据字节数

    // 校验参数合法性
    if (startAddr < Reg_num && 
        (startAddr + regCount) <= Reg_num && 
        byteCount == (regCount * 2)) {  // 字节数必须是寄存器数量的2倍

        // 写入多个寄存器数据
        for (j = 0; j < regCount; j++) {
            // 每个寄存器占2字节（高字节在前）
            Reg[startAddr + j] = (modbus.rcbuf[7 + 2*j] << 8) | modbus.rcbuf[8 + 2*j];
        }

        // 构建响应报文
        modbus.sendbuf[i++] = modbus.myadd;               // 从机地址
        modbus.sendbuf[i++] = 0x10;                       // 功能码10
        modbus.sendbuf[i++] = (startAddr >> 8) & 0xFF;    // 起始地址高字节
        modbus.sendbuf[i++] = startAddr & 0xFF;           // 起始地址低字节
        modbus.sendbuf[i++] = (regCount >> 8) & 0xFF;     // 写入数量高字节
        modbus.sendbuf[i++] = regCount & 0xFF;            // 写入数量低字节

        // 计算CRC
        crc = CRC16(modbus.sendbuf, i);
        modbus.sendbuf[i++] = crc & 0xFF;                 // CRC低字节
        modbus.sendbuf[i++] = (crc >> 8) & 0xFF;          // CRC高字节

        // 发送响应
        GPIO_SetBits(GPIOC, GPIO_Pin_7);                  // RS485发送使能
        uartWriteBlocking(modbus.sendbuf, i);             // 发送数据
        while (DMA_GetCurrDataCounter(DMA1_Channel4) != 0);  // 等待发送完成
        Delay_Ms(20);
        GPIO_ResetBits(GPIOC, GPIO_Pin_7);                // 切换回接收模式

        k = 0;  // 处理成功
    }

    // 清空缓冲区
    memset(modbus.rcbuf, 0, sizeof(modbus.rcbuf));
    memset(modbus.sendbuf, 0, sizeof(modbus.sendbuf));

    return (k == 0) ? 2 : 3;  // 与功能码6保持一致的返回值
}

// Modbus事件处理函数
uint8_t Modbus_Event(uint8_t *Buf, uint16_t cnt) {
    uint8_t a[2];
    uint16_t i;

    for (i = 0; i < cnt; i++) {
        if (Buf[i] == modbus.myadd) {
            // 根据功能码确定报文长度
            uint8_t funcCode = Buf[i + 1];
            uint8_t frameLen;

            // 功能码10的报文长度需要动态计算
            if (funcCode == 0x10) {
                frameLen = 9 + Buf[i + 6];  // 8字节固定头部 + 数据字节数
            } else {
                frameLen = 8;  // 功能码3和6的固定帧长
            }

            // 检查报文是否完整
            if (i + frameLen > cnt) break;

            // 复制完整报文到接收缓冲区
            memcpy(modbus.rcbuf, &Buf[i], frameLen);
            modbus.recount = frameLen;

            // 校验CRC
            a[1] = CRC16(modbus.rcbuf, modbus.recount - 2) >> 8;
            a[0] = CRC16(modbus.rcbuf, modbus.recount - 2) & 0xff;
            // printf("a1:%d  a2:%d\r\n",a[0],a[1]);
            if (a[1] == modbus.rcbuf[modbus.recount - 1] && 
                a[0] == modbus.rcbuf[modbus.recount - 2]) {

                if (modbus.rcbuf[0] == modbus.myadd) {
                    switch (modbus.rcbuf[1]) {
                        case 0x03:  // 功能码3：读寄存器
                            Modbus_Func3();
                            return 1;
                        case 0x06:  // 功能码6：写单个寄存器
                            return Modbus_Func6();
                        case 0x10:  // 功能码10：写多个寄存器
                            return Modbus_Func10();
                        default:
                            // 不支持的功能码
                            break;
                    }
                }
            }
        }
    }
    return 0;
}
    